﻿#region License and Copyright
// Copyright 2008 C5 Solutions AG, Tal 26, 80331 Munich, Germany
// 
// This file is part of QueuedSmtpClient project.
// 
// QueuedSmtpClient project is free software: you can redistribute 
// it and/or modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
// 
// QueuedSmtpClient project is distributed in the hope that it will be 
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License along with
// QueuedSmtpClient project. If not, see http://www.gnu.org/licenses/.
#endregion

using System;
using System.IO;
using System.Net.Mail;
using System.Threading;
using System.Xml.Serialization;

namespace c5solutions.tools.QueuedSmtpClient
{
    public class QueuedSmtpClient : SmtpClient
    {
        public string TempFolder { get; set; }
        private int mSleepIntervalSeconds;
        private static object locker = new object();
        Thread mHelperSendingSerializedMails;

        public delegate MailMessageErrorSolution SendMailErrorOccuredDelegate(object sender, SendMailErrorOccuredEventArgs args);

        public SendMailErrorOccuredDelegate mMailErrorOccuredDelegate;



        /// <summary>
        /// Creates a new queued smtpclient
        /// </summary>
        /// <param name="tempFolder">The folder which temporarily stores email messages if no network for sending is available.</param>
        /// <param name="host">The smtp server to use.</param>
        /// <param name="port">The smtp port. Usually, port 25.</param>
        /// <param name="sleepIntervalSeconds">The intervall the worker thread sleeps until it tries again to send mails.</param>
        public QueuedSmtpClient(string tempFolder, string host, int port, int sleepIntervalSeconds) : base(host, port)
        {
            TempFolder = tempFolder;
            mSleepIntervalSeconds = sleepIntervalSeconds;

            mHelperSendingSerializedMails = new Thread(TryToSend);
            mHelperSendingSerializedMails.Start();
        }

        public void Stop()
        {
            if (mHelperSendingSerializedMails != null)
                mHelperSendingSerializedMails.Abort();
        }

        public new void Send(MailMessage msg)
        {
            //don't send the message now, but serialize it and let a thread deserialize and send.
            SerializeMailMessage(msg);
        }

        public void TryToSend()
        {
            while (true)
            {
                lock (locker)
                {
                    string[] SerializedMailMessages = Directory.GetFiles(TempFolder);

                    XmlSerializer serializer = new XmlSerializer(typeof (SerializableMailMessage));

                    for (int i = 0; i < SerializedMailMessages.Length; i++)
                    {
                        SerializableMailMessage mailToDeserialize;

                        using (TextReader streamReader = new StreamReader(SerializedMailMessages[i]))
                        {
                            mailToDeserialize = serializer.Deserialize(streamReader) as SerializableMailMessage;
                        }

                        if (mailToDeserialize != null &&
                            System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
                        {
                            try
                            {
                                base.Send(mailToDeserialize.Email);

                                File.Delete(SerializedMailMessages[i]);
                            }
                            catch (Exception e)
                            {
                                if (mMailErrorOccuredDelegate != null)
                                {
                                    SendMailErrorOccuredEventArgs args = new SendMailErrorOccuredEventArgs(mailToDeserialize.Email, e);
                                    if (mMailErrorOccuredDelegate(this, args) == MailMessageErrorSolution.Delete)
                                    {
                                        File.Delete(SerializedMailMessages[i]);
                                    }
                                }
                            }
                        }
                    }
                }

                Thread.Sleep(mSleepIntervalSeconds * 1000);
            }
        }

        private void SerializeMailMessage(MailMessage msg)
        {
            lock (locker)
            {
                //serialize message to temp folder
                SerializableMailMessage mailToSerialize = new SerializableMailMessage();
                mailToSerialize.Email = msg;

                XmlSerializer serializer = new XmlSerializer(typeof (SerializableMailMessage));

                using (
                    TextWriter streamWriter = new StreamWriter(TempFolder + "\\" + System.DateTime.Now.Ticks + ".msg"))
                {
                    serializer.Serialize(streamWriter, mailToSerialize);
                }
            }
        }
    }

    public class SendMailErrorOccuredEventArgs : EventArgs
    {
        public MailMessage FailedMailMessage;
        public Exception RaisedException;

        public SendMailErrorOccuredEventArgs(MailMessage message, Exception exception)
        {
            FailedMailMessage = message;
            RaisedException = exception;
        }
    }

    public enum MailMessageErrorSolution
    {
        Delete,
        Ignore
    }
}
